--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 835667dba3f7f3e1145417190ae518cef0f7527e
Parents : f89f91e
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-24T11:39:59-05:00
feat: improve SettingsPage styles with deep-scoping and add libseccomp to Docker setup scripts
Changes
6 files changed, 80 insertions(+), 27 deletions(-)
Diff
diff --git a/meshchatx.rsm b/meshchatx.rsm
index 5b31af2a..baeaba34 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ
diff --git a/meshchatx/src/frontend/components/settings/SettingsPage.vue b/meshchatx/src/frontend/components/settings/SettingsPage.vue
index c8bb7ea2..bb253c54 100644
--- a/meshchatx/src/frontend/components/settings/SettingsPage.vue
+++ b/meshchatx/src/frontend/components/settings/SettingsPage.vue
@@ -5265,70 +5265,74 @@ export default {
<style scoped>
@reference "../../style.css";
-.settings-section {
+/*
+ * Shared settings chrome is used by extracted section components.
+ * :deep is required so scoped styles still reach their markup.
+ */
+:deep(.settings-section) {
@apply w-full border-b border-gray-200/60 dark:border-zinc-800/60 py-6 sm:py-8 flex flex-col break-inside-avoid;
}
-.settings-section--hero {
+:deep(.settings-section--hero) {
@apply border-b border-gray-200/60 dark:border-zinc-800/60 py-6 sm:py-8;
}
-.settings-section__header {
+:deep(.settings-section__header) {
@apply flex items-center justify-between gap-3 pb-4 border-b border-gray-100/60 dark:border-zinc-800/60;
}
-.settings-section__header h2 {
+:deep(.settings-section__header h2) {
@apply text-lg font-semibold text-gray-900 dark:text-white;
}
-.settings-section__header p {
+:deep(.settings-section__header p) {
@apply text-sm text-gray-600 dark:text-gray-400;
}
-.settings-section__eyebrow {
+:deep(.settings-section__eyebrow) {
@apply text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400;
}
-.settings-section__body {
+:deep(.settings-section__body) {
@apply pt-4 text-gray-900 dark:text-gray-100;
}
-.input-field {
+:deep(.input-field) {
@apply bg-gray-50/90 dark:bg-zinc-800/80 border border-gray-200 dark:border-zinc-700 text-sm rounded-2xl focus:ring-2 focus:ring-blue-400 focus:border-blue-400 dark:focus:ring-blue-500 dark:focus:border-blue-500 block w-full p-2.5 text-gray-900 dark:text-gray-100 transition;
}
-.btn-maintenance {
+:deep(.btn-maintenance) {
@apply w-full px-4 py-3 rounded-2xl border transition flex items-center justify-between;
}
-.setting-toggle {
- @apply relative flex flex-row-reverse items-center gap-3 rounded-2xl border border-gray-200 dark:border-zinc-800 bg-white/70 dark:bg-zinc-900/70 px-3 py-3;
+:deep(.setting-toggle) {
+ @apply relative flex flex-row-reverse items-start gap-3 rounded-2xl border border-gray-200 dark:border-zinc-800 bg-white/70 dark:bg-zinc-900/70 px-3 py-3;
}
-.setting-toggle > :deep(label) {
+:deep(.setting-toggle > label) {
@apply shrink-0 self-center;
}
-.setting-toggle :deep(.sr-only) {
+:deep(.setting-toggle .sr-only) {
@apply absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap border-0;
}
-.setting-toggle__label {
+:deep(.setting-toggle__label) {
@apply flex-1 min-w-0 flex flex-col gap-0.5;
}
-.setting-toggle__title {
- @apply text-sm font-semibold text-gray-900 dark:text-white break-words;
+:deep(.setting-toggle__title) {
+ @apply text-sm font-semibold text-gray-900 dark:text-white break-words leading-snug;
}
-.setting-toggle__description {
- @apply text-sm text-gray-600 dark:text-gray-300 break-words;
+:deep(.setting-toggle__description) {
+ @apply text-xs sm:text-sm text-gray-600 dark:text-gray-300 break-words leading-snug;
}
-.setting-toggle__hint {
+:deep(.setting-toggle__hint) {
@apply text-xs text-gray-500 dark:text-gray-400 break-words;
}
-.info-callout {
+:deep(.info-callout) {
@apply rounded-2xl border border-blue-100 dark:border-blue-900/40 bg-blue-50/60 dark:bg-blue-900/20 px-3 py-3 text-blue-900 dark:text-blue-100;
}
-.monospace-field {
+:deep(.monospace-field) {
font-family: "Roboto Mono", monospace;
}
-.address-card {
+:deep(.address-card) {
@apply relative border border-gray-200/70 dark:border-zinc-800/80 py-3 px-3 sm:rounded-xl sm:bg-black/2 dark:sm:bg-white/2 space-y-2;
}
-.address-card__label {
+:deep(.address-card__label) {
@apply text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400;
}
-.address-card__value {
+:deep(.address-card__value) {
@apply text-sm text-gray-900 dark:text-white wrap-break-word pr-16;
}
-.address-card__action {
+:deep(.address-card__action) {
@apply absolute top-3 right-3 inline-flex items-center gap-1 rounded-full border border-gray-200 dark:border-zinc-700 px-3 py-1 text-xs font-semibold text-gray-700 dark:text-gray-100 bg-white/70 dark:bg-zinc-900/60 hover:border-blue-400 dark:hover:border-blue-500 transition;
}
.fade-enter-active,
diff --git a/scripts/docker/runtime-setup-chainguard.sh b/scripts/docker/runtime-setup-chainguard.sh
index 609c96c6..1d60679e 100755
--- a/scripts/docker/runtime-setup-chainguard.sh
+++ b/scripts/docker/runtime-setup-chainguard.sh
@@ -3,7 +3,7 @@
# Chainguard/Wolfi final-stage packages, pip bootstrap, and meshchat user.
set -eu
-apk add --no-cache opus libffi shadow
+apk add --no-cache opus libffi shadow libseccomp
pip install --no-cache-dir --upgrade "pip>=26.0" "setuptools" "jaraco.context>=6.1.0"
rm -rf /root/.cache/pip
diff --git a/scripts/docker/runtime-setup.sh b/scripts/docker/runtime-setup.sh
index a716b5e0..b85c6884 100755
--- a/scripts/docker/runtime-setup.sh
+++ b/scripts/docker/runtime-setup.sh
@@ -7,7 +7,7 @@ set -eu
VARIANT="${1:-${VARIANT:-standard}}"
apk upgrade --no-cache
-apk add --no-cache opusfile libffi espeak-ng su-exec
+apk add --no-cache opusfile libffi espeak-ng su-exec libseccomp
case "${VARIANT}" in
standard) ;;
diff --git a/tests/frontend/behaviorContracts.test.js b/tests/frontend/behaviorContracts.test.js
index e4afda3c..82c5944f 100644
--- a/tests/frontend/behaviorContracts.test.js
+++ b/tests/frontend/behaviorContracts.test.js
@@ -105,6 +105,20 @@ describe("behavior contracts: user-visible wiring must stay connected", () => {
expect(util).toContain("LOCAL_BACKEND_URL");
});
+ it("SettingsPage deep-scopes shared toggle styles for extracted sections", () => {
+ const src = readSource("meshchatx/src/frontend/components/settings/SettingsPage.vue");
+ expect(src).toContain(":deep(.setting-toggle)");
+ expect(src).toContain(":deep(.setting-toggle__label)");
+ expect(src).toContain(":deep(.setting-toggle__title)");
+ });
+
+ it("Docker runtime images install libseccomp for Seccomp-BPF", () => {
+ const alpine = readSource("scripts/docker/runtime-setup.sh");
+ const chainguard = readSource("scripts/docker/runtime-setup-chainguard.sh");
+ expect(alpine).toMatch(/apk add[^\n]*libseccomp/);
+ expect(chainguard).toMatch(/apk add[^\n]*libseccomp/);
+ });
+
it("Android backup of app data is disabled", () => {
const src = readSource("android/app/src/main/AndroidManifest.xml");
expect(src).toMatch(/android:allowBackup\s*=\s*"false"/);
diff --git a/tests/frontend/settingsSections.test.js b/tests/frontend/settingsSections.test.js
index 989a25a3..45976b3c 100644
--- a/tests/frontend/settingsSections.test.js
+++ b/tests/frontend/settingsSections.test.js
@@ -104,6 +104,41 @@ describe("TelephonySettingsSection", () => {
});
describe("AppearanceSettingsSection", () => {
+ it("renders glass and split-view toggles as stacked setting rows", () => {
+ const wrapper = mount(AppearanceSettingsSection, {
+ props: {
+ visible: true,
+ config: {
+ theme: "dark",
+ messages_sidebar_position: "left",
+ message_font_size: 14,
+ message_icon_size: 28,
+ ui_transparency: 0,
+ ui_glass_enabled: true,
+ messages_multi_pane_enabled: true,
+ nomad_tabs_enabled: true,
+ rrc_enabled: true,
+ rrc_unread_badges_enabled: true,
+ },
+ detailedOutboundSendStatus: false,
+ messageIconPreviewStyle: { width: "28px", height: "28px" },
+ },
+ global: {
+ mocks: { $t: (key) => key },
+ stubs: {
+ Toggle: {
+ template: '<button type="button" class="toggle-stub" />',
+ },
+ MaterialDesignIcon: true,
+ },
+ },
+ });
+ const toggles = wrapper.findAll("label.setting-toggle");
+ expect(toggles.length).toBeGreaterThanOrEqual(4);
+ expect(toggles[0].find(".setting-toggle__title").text()).toContain("ui_glass_enabled");
+ expect(toggles[0].find(".setting-toggle__description").text()).toContain("ui_glass_enabled_description");
+ });
+
it("emits theme and field events without mutating config prop", async () => {
const config = {
theme: "light",
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────